package com.kt.coroutinedemo.Netty

import android.util.Log
import com.google.gson.Gson
import com.kt.coroutinedemo.MainActivity
import com.kt.coroutinedemo.utils.BaseFileCommand
import com.kt.coroutinedemo.utils.DecodeUtils
import com.kt.coroutinedemo.utils.GlobalConstant
import com.kt.coroutinedemo.utils.PacketUtils
import com.streamax.app.cqbus.platform.ext.toByteArray
import com.streamax.app.cqbus.platform.socket.tcp.command.TcpCommand01
import com.streamax.app.cqbus.platform.socket.tcp.command.TcpCommand02
import com.streamax.app.cqbus.platform.socket.tcp.command.TcpCommand03
import com.streamax.app.cqbus.platform.socket.tcp.command.TcpResponse81
import com.streamax.app.cqbus.platform.socket.tcp.command.TcpResponse82
import com.swallowsonny.convertextlibrary.toHexString
import io.netty.bootstrap.Bootstrap
import io.netty.channel.Channel
import io.netty.channel.ChannelId
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.FixedLengthFrameDecoder
import io.netty.handler.codec.LengthFieldBasedFrameDecoder
import io.netty.handler.logging.LogLevel
import io.netty.handler.logging.LoggingHandler
import io.netty.handler.timeout.IdleStateHandler
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import java.io.File
import java.io.FileOutputStream
import java.net.InetSocketAddress
import kotlinx.coroutines.channels.Channel as CoroutineChannel
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit


/**
 * @Author Xzh
 * @Date 2025/6/3
 * @Description
 */
object NettyClient {

    val TAG = "NettyClient"

    var serviceIp = "47.108.161.232"
    var servicePort = 801

    /**
     * 当前连接的通道对象，用于与服务器进行数据通信
     */
     var channel: Channel? = null

    private var bootstrap: Bootstrap? = null

    private var group: NioEventLoopGroup? = null


    /**
     * 连接TCP
     */
    fun client(ip: String = serviceIp, port: Int = servicePort, listener: LinkListener) {
        serviceIp = ip
        servicePort = port
        connect(listener)
    }

    /**
     * 断开连接
     */
    fun closeClient() {
//        group?.shutdownGracefully()
//        channelMap.map {
//            Log.e(TAG, "closeClient: id${it.key}")
//            it.value.close()
//        }
        Log.e(TAG, "关闭连接: id${channel?.id()}")
        channel?.close()
    }

    fun sendMsg(bytes: ByteArray) {
        if (channel == null){

        }
        channel?.let {
            if (it.isWritable) {
                it.writeAndFlush(bytes).addListener {
                    if (it.isSuccess) {
                        Log.i(TAG, "sendMsg: 发送成功")
                    } else {
                        Log.i(TAG, "sendMsg: 发送失败")
                    }
                }
            }
        }
    }
    // 存储请求ID与响应通道的映射
    private val responseChannels = ConcurrentHashMap<String, CoroutineChannel<ByteArray>>()

    // 添加一个新的 sendMsg 方法重载
    fun sendMsg(bytes: ByteArray, needResponse: Boolean = false): Deferred<ByteArray?>? {
        if (!needResponse) {
            // 不需要等待响应的简单发送
            sendMsg(bytes)
            return null
        }
        if (channel != null){
            // 需要等待响应的情况，使用chanelId作为映射id
            val requestId = channel?.id()?.asLongText()
            return sendMsg(bytes, requestId!!)

        }
        return null
    }

    private fun sendMsg(bytes: ByteArray, requestId: String): Deferred<ByteArray?> {
        val deferred = CompletableDeferred<ByteArray?>()

        if (channel == null) {
            deferred.completeExceptionally(IllegalStateException("Channel is not available"))
            return deferred
        }

        // 创建响应通道并存储
        val responseChannel = CoroutineChannel<ByteArray>(1)
        responseChannels[requestId] = responseChannel

        channel?.let {
            if (it.isWritable) {
                it.writeAndFlush(bytes).addListener { future ->
                    if (future.isSuccess) {
                        Log.i(TAG, "sendMsg: 发送成功")
                        // 启动协程等待响应
                        GlobalScope.launch {
                            try {
                                // 设置超时时间
                                withTimeout(10000) { // 10秒超时
                                    val response = responseChannel.receive()
                                    deferred.complete(response)
                                }
                            } catch (e: TimeoutCancellationException) {
                                deferred.completeExceptionally(RuntimeException("Response timeout"))
                                Log.i(TAG, "移除通道：${requestId}")
                                responseChannel.close()
                            }
                        }
                    } else {
                        Log.i(TAG, "sendMsg: 发送失败")
                        deferred.completeExceptionally(future.cause() ?: RuntimeException("Send failed"))
                        responseChannels.remove(requestId)
                        responseChannel.close()
                    }
                }
            } else {
                deferred.completeExceptionally(IllegalStateException("Channel is not writable"))
                responseChannel.close()
            }
        }

        return deferred
    }

    // 在ClientHandler中调用此方法来传递响应数据
    fun handleResponse(requestId: String, responseData: ByteArray) {
        responseChannels[requestId]?.let { channel ->
            GlobalScope.launch {
                channel.send(responseData)
            }
        }
    }




    /**
     * 连接TCP
     */
    private fun connect(listener: LinkListener) {
        try {
            //事件循环组（线程池），负责处理所有 I/O 操作和任务调度,单线程模式（推荐移动端使用）
            group = NioEventLoopGroup(1)
            //客户端启动辅助类，用于配置和启动 Netty 客户端
            bootstrap = Bootstrap()
                //指定channel类型
                .channel(NioSocketChannel::class.java)
                //指定EventLoopGroup
                .group(group)
                //指定Handler
                .handler(object : ChannelInitializer<SocketChannel>() {
                    override fun initChannel(ch: SocketChannel?) {
                        ch?.pipeline()?.let { pipeline ->
                            pipeline.addLast( KtLengthFieldBasedFrameDecoder())
                            //添加发送数据编码器
                            pipeline.addLast(ClientEncoder())
                            //添加数据处理器
                            pipeline.addLast(ClientHandler(listener))
                            // 开启日志，可以设置日志等级
                            pipeline.addLast(LoggingHandler(LogLevel.INFO))
                            // ===== 1. 添加超时处理器 =====
//                            // 空闲状态检测（30秒读空闲，15秒写空闲）
//                            pipeline.addLast(
//                                "idleStateHandler",
//                                IdleStateHandler(60, 15, 0, TimeUnit.SECONDS)
//                            )
//                            // 读超时（60秒未收到数据）
//                            pipeline.addLast(
//                                "readTimeoutHandler",
//                                ReadTimeoutHandler(60, TimeUnit.SECONDS)
//                            )
//                            // 写超时（10秒未完成写操作）
//                            pipeline.addLast(
//                                "writeTimeoutHandler",
//                                WriteTimeoutHandler(10, TimeUnit.SECONDS)
//                            )
//                            // ===== 2. 添加空闲事件处理器 =====
//                            pipeline.addLast("idleEventHandler", IdleStateEventHandler())
                        }
                    }
                })
            //连接到服务端
            val future = bootstrap?.connect(InetSocketAddress(serviceIp, servicePort))
            //获取连接通道
            channel = future?.sync()?.channel()

            Log.e(TAG, "连接id: ${channel?.id()}")

        } catch (e: Exception) {
            listener.linkFailed()
            Log.e(TAG, "connect: 连接失败：${e.message}")
            e.printStackTrace()
        }
    }


    /**
     * 下载线路文件
     * @param lineName 线路名称
     * @param channelId 选择现在线路的管道id
     * @param listener 下载监听
     */
    fun downLoadLine(lineName: String, channelId: ChannelId, listener: DownloadFileListener) {
        //判断是否连接
        if (channel == null) {
            listener.downloadFailed("请先连接服务器")
            return
        }
        //下载文件
        GlobalScope.launch {
            try {
                val tc01 = TcpCommand01(lineCode = "818")
                val request01 = PacketUtils.baseFilePacket(0x01, GlobalConstant.getSuNumber(), tc01.packet())
                Log.e(TAG, "TCP文件下载登录 步骤1:${request01.toHexString()}")
                // 发送请求并等待响应
                val response81 = sendMsg(request01, needResponse = true)?.await() ?: throw Exception("接收81指令超时!")
                Log.i(TAG, "<<<<<<<<<<<<:${response81.toHexString()} ")
                //解析81指令
                val decoded81 = BaseFileCommand(0, 0, "", 0, (0).toByteArray)
                DecodeUtils.baseEnFilePackage(response81, decoded81)
                val tcp81 = TcpResponse81().decode(decoded81.content)
                if (tcp81.result.toInt() == 0x00) {
                    Log.e(
                        TAG,
                        "---------------------------TCP文件下载登录成功---------------------------"
                    )
                    val tc02 =
                        TcpCommand02(downloadType = 0x01, packetSize = MainActivity.singlePackSize, msg = lineName)
                    //发送下载请求
                    val p2 = PacketUtils.baseFilePacket(
                        tc02.commandId.toByte(),
                        GlobalConstant.getSuNumber(),
                        tc02.packet()
                    )
                    Log.e(TAG, "TCP文件下载包信息请求 步骤2:${p2.toHexString()}")
                    // 发送请求并等待响应
                    val response82 = NettyClient.sendMsg(p2, needResponse = true)?.await()
                        ?: throw Exception("接收82指令超时!")
                    Log.i(TAG, "<<<<<<<<<<<<:${response82.toHexString()} ")
                    //解析82指令
                    val decode82 = BaseFileCommand(0, 0, "", 0, (0).toByteArray)
                    DecodeUtils.baseEnFilePackage(response82, decode82)
                    val tcp82 = TcpResponse82().decode(decode82.content)
                    if (tcp82.result.toInt() == 0x00) {
                        Log.e(
                            TAG,
                            "---------------------------TCP文件请求成功---------------------------"
                        )
                        Log.e(TAG, "nettySend: ${Gson().toJson(tcp82)}")
                        downloadFile(tcp82)?.let {
                            //错误信息
                            Log.e(TAG, "错误信息:${it.message}")
                        }
                    }
                }

            }catch (e: Exception) {
                Log.e(TAG, "等待响应时出错: ${e.message}")
                listener.downloadFailed("等待响应时出错: ${e.message}")
            }
        }

    }


    /**
     * 下载文件
     */
    private fun downloadFile(response82: TcpResponse82): Throwable? {
        var error: Throwable? = null
        runBlocking {
            try {
                val totalPackets = response82.mPackCount
                var currentPacketIndex = 1
                // 创建文件输出流，直接写入文件
                val file = createUniqueFile(MainActivity.defaultDownFilePath, response82.mFileName)
                val fileOutputStream = FileOutputStream(file)
                val token = response82.mToken
                try {
                    while (currentPacketIndex <= totalPackets) {
                        // 构造请求包
                        val packet = getDownLoadPacket(token, currentPacketIndex)

                        Log.e(
                            TAG,
                            "请求文件数据包 序号:${currentPacketIndex}, 数据:${packet.toHexString()}"
                        )

                        // 发送请求并等待响应
                        val responseData = sendMsg(packet, needResponse = true)?.await()

                        if (responseData == null) {
                            error = Exception("未收到第${currentPacketIndex}个数据包的响应")
                            break
                        }

                        // 解析响应数据
                        val decodedPacket = BaseFileCommand(0, 0, "", 0, (0).toByteArray)
                        DecodeUtils.baseEnFilePackage(responseData, decodedPacket)

//                    // 直接写入文件，而不是存储在内存中
//                    fileOutputStream.write(decodedPacket.content)
                        currentPacketIndex++

                        Log.e(TAG, "成功接收第${currentPacketIndex}个数据包")
                    }
                    Log.e(TAG, "下载完成")

                    if (error == null) {
                        Log.e(TAG, "文件下载完成并保存到本地")
                    } else {

                    }
                } finally {
                    fileOutputStream.close()
                }

            } catch (e: Exception) {
                error = e
                Log.e(TAG, "下载文件时出错: ${e.message}")
                //删除文件
                val file = File(MainActivity.defaultDownFilePath + response82.mFileName)
                if (file.exists()) {
                    file.delete()
                } else {

                }
            }
        }
        return error
    }

    /**
     * 创建唯一文件名，避免文件冲突
     */
    private fun createUniqueFile(directoryPath: String, fileName: String): File {
        val directory = File(directoryPath)

        // 确保目录存在
        if (!directory.exists()) {
            if (!directory.mkdirs()) {
                Log.w(TAG, "无法创建目录: $directoryPath")
            } else {
                Log.d(TAG, "成功创建目录: $directoryPath")
            }
        }

        var file = File(directory, fileName)
        var counter = 1
        val fileNameWithoutExtension = fileName.substringBeforeLast(".", fileName)
        val extension = fileName.substringAfterLast(".", "")

        // 循环检查文件是否存在，最多尝试1000次
        while (file.exists() && counter <= 1000) {
            val newFileName = if (extension.isNotEmpty()) {
                "${fileNameWithoutExtension}_$counter.$extension"
            } else {
                "${fileNameWithoutExtension}_$counter"
            }
            file = File(directory, newFileName)
            counter++
        }

        // 如果尝试了1000次仍然存在，使用时间戳
        if (file.exists()) {
            val timestamp = System.currentTimeMillis()
            val newFileName = if (extension.isNotEmpty()) {
                "${fileNameWithoutExtension}_${timestamp}.$extension"
            } else {
                "${fileNameWithoutExtension}_${timestamp}"
            }
            file = File(directory, newFileName)
        }

        Log.d(TAG, "最终文件路径: ${file.absolutePath}")
        return file
    }

    /**
     * 获取下载包指令
     */
    private fun getDownLoadPacket(mToken: Int, packetSn: Int): ByteArray {
        val com03 = TcpCommand03(mToken, packetSn)
        return PacketUtils.baseFilePacket(0x03, GlobalConstant.getSuNumber(), com03.packet())
    }

}


